package io.kiki.stack.netty.channelManager;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
@Setter
@Getter
public class Pool {

    private final CopyOnWriteArrayList<Channel> channelList;
    private final ChannelSelectStrategy channelSelectStrategy;
    private volatile long lastAccessTimestamp;
    private volatile boolean asyncCreationDone;

    public Pool(ChannelSelectStrategy channelSelectStrategy) {
        this.channelSelectStrategy = channelSelectStrategy;
        this.channelList = new CopyOnWriteArrayList<Channel>();
        this.lastAccessTimestamp = System.currentTimeMillis();
        this.asyncCreationDone = true;
    }

    public void add(Channel channel) {
        markAccess();
        if (null == channel) {
            return;
        }
        boolean res = channelList.addIfAbsent(channel);
        if (res) {
            channel.increaseReferenceCount();
        }
    }


    public boolean contains(Channel channel) {
        return channelList.contains(channel);
    }

    public void removeAllAndTryClose() {
        for (Channel channel : channelList) {
            removeAndTryClose(channel);
        }
        channelList.clear();
    }

    public void removeAndTryClose(Channel channel) {
        if (null == channel) {
            return;
        }
        boolean removed = channelList.remove(channel);
        if (removed) {
            channel.decreaseReferenceCount();
        }
        if (channel.noReference()) {
            channel.close();
        }
    }


    public Channel get() {
        if (null != channelList) {
            List<Channel> list = new ArrayList<Channel>(channelList);
            if (list.size() > 0) {
                return channelSelectStrategy.select(list);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    public List<Channel> getAll() {
        return new ArrayList<Channel>(channelList);
    }

    public int size() {
        return channelList.size();
    }

    public boolean isEmpty() {
        return channelList.isEmpty();
    }

    public long getLastAccessTimestamp() {
        return lastAccessTimestamp;
    }

    /**
     * do mark the time stamp when access this pool
     */
    private void markAccess() {
        lastAccessTimestamp = System.currentTimeMillis();
    }

    /**
     * is async create Channel done
     *
     * @return true if async create Channel done
     */
    public boolean isAsyncCreationDone() {
        return asyncCreationDone;
    }

    /**
     * do mark async create Channel done
     */
    public void markAsyncCreationDone() {
        asyncCreationDone = true;
    }

    /**
     * do mark async create Channel start
     */
    public void markAsyncCreationStart() {
        asyncCreationDone = false;
    }


    public void scan() {
        if (null != channelList && !channelList.isEmpty()) {
            for (Channel channel : channelList) {
                if (!channel.isFine()) {
                    log.warn("Remove bad Channel when scanning conns of ChannelPool - {}:{}", channel.getRemoteIp(), channel.getRemotePort());
                    channel.close();
                    removeAndTryClose(channel);
                }
            }
        }
    }
}
